#include<vector>
#include<string>
#include<cstdlib>
#include<functional>
#include<iostream>
#include<string>
#include<fstream>
#include<cstdlib>
#include<boost/thread/thread.hpp>
#include<boost/program_options.hpp>
#include"sproxy.hpp"
#include<boost/date_time/posix_time/posix_time.hpp>


using namespace std;

namespace mainpo {
  vector<string> proxys_urls, conf_files, sfreeipnets;
  string testhost;
  string appname, defconf_file;
  string helpstr;
  int    dohelp;
  string outfile;
}

void init_res()
{
#ifdef WIN32
  WORD wVersionRequested;
  WSADATA wsaData;
  wVersionRequested = MAKEWORD(2, 0);
  if (WSAStartup(wVersionRequested, &wsaData) != 0) {
    exit(13);
  }
#endif
}

void dest_res()
{
#ifdef WIN32
  WSACleanup();
#endif
}

void parse_arg(int argc, char*argv[])
{
  namespace po = boost::program_options;
  using namespace sproxys;
  po::options_description genops("Generic options"),
    confops("Conf file options"),
    cmdops("Command line options");
  genops.add_options()
    ("testurl", po::value<string>(&testurl), "test url")
    ("teststr", po::value<string>(&teststr), "test string")
    ("checker-timeout", po::value<int>(&checker_timeout)->default_value(20000),
     "proxy checker timeout")
    ("fetcher-timeout", po::value<int>(&fetcher_timeout)->default_value(20000),
     "proxys fetcher timeout")
    ("outfile", po::value<string>(&mainpo::outfile), "write result to file")
    ("freeipnet", po::value< vector<string> >(&mainpo::sfreeipnets)->composing(),
     "add a 'ip mask' as a free ipnet")
    ("proxys-url", po::value< vector<string> >(&mainpo::proxys_urls)->composing(),
     "proxys list url[http or file], format:\n"
     "\turl extract-regex replace-template [postdata]")
    ("verbose,v", po::value<int>(&verbose)->default_value(1), "verbose control")
    ("genpac", po::value<string>(&genpac), "gen a .pac file for give proxy")
    ("pacfile", po::value<string>(&pacfile), "save the .pac file for --genpac or top proxy")
    ("ncheckers", po::value<int>(&ncheckers)->default_value(10), "set # of checkers")
    ;
  cmdops.add_options()
    ("help,h", "generate the help message")
    ("conf", po::value< vector<string> >(), "read a conf file")
    ("noconf", "prevent reading conf file, even given")
    ("testhost", po::value<string>(&mainpo::testhost), "see if host is in freenet")
    ;
  cmdops.add(genops);
  confops.add(genops);
  ostringstream oss;
  oss<<cmdops<<endl;
  mainpo::helpstr=oss.str();
  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, cmdops), vm);
  if (vm.count("noconf")==0) {
    vector<string> confs;
    if(vm.count("conf")) {
      confs = vm["conf"].as<vector<string> >();
    } else {
      confs.push_back(mainpo::defconf_file);
    }
    for (vector<string>::iterator it=confs.begin(); it!=confs.end(); ++it) {
      ifstream ifs(it->c_str());
      try {
        if (ifs.good())
          po::store(po::parse_config_file(ifs, confops), vm);
      } catch(exception& e) {
        cerr<<"parse conf "<<*it<<" failed:"<<e.what()<<endl;
      }
    }
  }
  po::notify(vm);
  mainpo::dohelp=vm.count("help");
}

void print_help(int r)
{
  cerr<<mainpo::appname<<": fetch and check proxys, then give a proxy-time list"<<endl;
  cerr<<mainpo::helpstr<<endl;
  cerr<<"non warranty"<<endl;
  if (r>=0) exit(r);
}

// a compose option is:
// list of string encoded by ""
// or: " space hold " raw_is_ok_if_no_space  "see \"lk'j "
void decompose_option(const string&sv, vector<string>&v)
{
  v.clear();
  if (sv.empty()) return;
  string::const_iterator po, pe, pae, pbe, ps;
  bool qted = false;
  pe = sv.end();
  po = find_if(sv.begin(), pe, not1(bind2nd(equal_to<char>(), ' ')));
  while (po < pe) {
    qted = false;
    if (*po == '"') {
      ++po;
      qted = true;
    }
    if (qted) {
      string iv;
      ps = po;
      while (ps != pe) {
        if (*ps=='"') {
          ++ps;
          break;
        }
        else if (*ps == '\\') {
          ++ps;
          if (ps != pe) {
            if(*ps =='"') 
              iv.push_back('"');
            else if(*ps == '\\')
              iv.push_back('\\');
            else {
              iv.push_back('\\');
              iv.push_back(*ps);
            }
          } else break;
        } else
          iv.push_back(*ps);
        ++ps;
      }
      v.push_back(iv);
    } else {
      ps = find(po, pe, ' ');
      v.push_back(string(po, ps));
    }
    po = find_if(ps, pe, not1(bind2nd(equal_to<char>(), ' ')));
  }
}

int main(int argc, char*argv[]) {
  using namespace mainpo;
  using namespace sproxys;
  appname=argv[0];
#ifdef WIN32
  defconf_file = appname+".ini";
#else
  defconf_file = appname+".conf";
#endif  
  try {
    parse_arg(argc, argv);
  } catch (exception& e) {
    cerr<<"error command line?: "<<e.what()<<endl;
    print_help(13);
  }

  if (dohelp) print_help(1);
  
  //set fetcherconfs
  for(vector<string>::iterator it=proxys_urls.begin(); it!=proxys_urls.end(); ++it) {
    vector<string> fi;
    decompose_option(*it, fi);
    if (fi.size() >= 3 && fi.size()<=4) {
      string tmp;
      if (fi.size()==4) tmp = fi[3];
      fetcherconfs.push_back(FetcherConf(fi[0], fi[1], fi[2], tmp));
    } else {
      if (verbose>8) cerr<<"proxys-url error format:"<<*it<<endl;
    }
  }
  proxys_urls.clear();
  
  for(vector<string>::iterator it=sfreeipnets.begin(); it!=sfreeipnets.end(); ++it) {
    freeipnets.push_back(Ipnet(*it));
  }
  if (sfreeipnets.size()) sort(freeipnets.begin(),freeipnets.end());
  sfreeipnets.clear();
  
  init_res();
  atexit(dest_res);
       
  if (verbose) {
    cerr<<"innner status:"<<endl
         <<"fetcher_timeout="<<fetcher_timeout<<endl
         <<"checker_timeout="<<checker_timeout<<endl
         <<"ncheckers="<<ncheckers<<endl
         <<"testurl="<<testurl<<endl
         <<"teststr="<<teststr<<endl
         <<"pacfile="<<pacfile<<endl;
     copy(sproxys::fetcherconfs.begin(), sproxys::fetcherconfs.end(),
          ostream_iterator<FetcherConf>(cout, "\n"));
  }
  
  if (genpac.size()) {
    if (pacfile.size()) {
      ofstream ofs(pacfile.c_str());
      gen_pac(ofs, genpac);
    } else {
      gen_pac(cout, genpac);
    }
    return 0;
  }

  if (testhost.size()) {
    try {
      TcpHost host(Url(testhost).host, 80);
      if (is_freeip(host)) {
        cout<<"host is free:"<<testhost<<endl;
      } else
        cout<<"host is unfree:"<<testhost<<endl;        
    } catch(exception& e) {
      cerr<<"error:"<<e.what()<<endl;
      return 1;
    }
    return 0;
  }

  if (testurl.empty() || teststr.empty() || fetcherconfs.size()==0) {
    cerr<<"I need something, try to give me more or change the conf file, ..."<<endl
        <<"such as testurl, teststr, or fetchers:url\n"<<endl;
    print_help(3);
  }
  helpstr.clear();
  
  boost::thread thfetcher(fetch_proxys);
  thfetcher.join();
  
  for (unsigned i=0; i<sproxys::proxys.size(); ++i) {
    Url url(proxys[i]);
    cerr<<"url:"<<url.to_string()<<endl;
  }
  return 0;
}
